package org.chen.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 文件上传服务
 */
@Service
@Slf4j
public class FileUploadService {

    @Value("${upload.base-dir:/www/wwwroot/files.flyingwaves.co/heygem_data}")
    private String baseDir;

    @Value("${upload.model-dir:face2face/temp}")
    private String modelDir;

    @Value("${upload.audio-dir:voice/data/origin_audio}")
    private String audioDir;

    @Value("${upload.thumbnail-dir:face2face/thumbnails}")
    private String thumbnailDir;

    private Path modelPath;
    private Path audioPath;
    private Path thumbnailPath;

    @PostConstruct
    public void init() throws IOException {
        modelPath = Paths.get(baseDir, modelDir);
        audioPath = Paths.get(baseDir, audioDir);
        thumbnailPath = Paths.get(baseDir, thumbnailDir);

        // 确保目录存在
        if (!Files.exists(modelPath)) {
            Files.createDirectories(modelPath);
            log.info("创建模型目录: {}", modelPath);
        }

        if (!Files.exists(audioPath)) {
            Files.createDirectories(audioPath);
            log.info("创建音频目录: {}", audioPath);
        }

        if (!Files.exists(thumbnailPath)) {
            Files.createDirectories(thumbnailPath);
            log.info("创建缩略图目录: {}", thumbnailPath);
        }
    }

    /**
     * 上传模型视频文件
     *
     * @param file     视频文件
     * @param filename 指定的文件名（可选）
     * @return 保存的文件路径和缩略图路径的Map
     * @throws IOException 如果文件操作失败
     */
    public Map<String, String> uploadModelVideo(MultipartFile file, String filename) throws IOException {
        String videoPath = uploadFile(file, modelPath, filename);
        Map<String, String> result = new HashMap<>();
        result.put("videoPath", videoPath);

        if (isVideoFile(file)) {
            // 只为视频文件生成缩略图
            String thumbnailFilename = getThumbnailFilename(videoPath);
            String thumbnailPath = generateThumbnail(Paths.get(baseDir, modelDir, videoPath).toString(), thumbnailFilename);
            result.put("thumbnailUrl", thumbnailPath);
        } else {
            // 如果不是视频文件，不生成缩略图
            log.info("文件不是视频文件，跳过生成缩略图: {}", file.getOriginalFilename());
            result.put("thumbnailUrl", "");
        }

        return result;
    }

    /**
     * 判断文件是否为视频文件
     *
     * @param file 文件
     * @return 是否为视频文件
     */
    private boolean isVideoFile(MultipartFile file) {
        // 检查MIME类型
        String contentType = file.getContentType();
        boolean mimeTypeIsVideo = contentType != null && contentType.startsWith("video/");

        // 检查文件扩展名
        String originalFilename = file.getOriginalFilename();
        boolean extensionIsVideo = false;
        if (originalFilename != null) {
            String lowerCaseFilename = originalFilename.toLowerCase();
            extensionIsVideo = lowerCaseFilename.endsWith(".mp4") ||
                    lowerCaseFilename.endsWith(".avi") ||
                    lowerCaseFilename.endsWith(".mov") ||
                    lowerCaseFilename.endsWith(".wmv") ||
                    lowerCaseFilename.endsWith(".flv") ||
                    lowerCaseFilename.endsWith(".mkv") ||
                    lowerCaseFilename.endsWith(".webm");
        }

        // 只有当MIME类型或扩展名表明是视频文件时才返回true
        return mimeTypeIsVideo || extensionIsVideo;
    }

    /**
     * 上传音频文件
     *
     * @param file     音频文件
     * @param filename 指定的文件名（可选）
     * @return 保存的文件路径
     * @throws IOException 如果文件操作失败
     */
    public String uploadAudio(MultipartFile file, String filename) throws IOException {
        return uploadFile(file, audioPath, filename);
    }

    /**
     * 上传文件到指定目录
     *
     * @param file      文件
     * @param directory 目标目录
     * @param filename  指定的文件名（可选）
     * @return 保存的文件路径
     * @throws IOException 如果文件操作失败
     */
    private String uploadFile(MultipartFile file, Path directory, String filename) throws IOException {
        if (file.isEmpty()) {
            throw new IOException("上传的文件为空");
        }

        // 确保目录存在
        if (!Files.exists(directory)) {
            Files.createDirectories(directory);
        }

        // 确定文件名
        String originalFilename = file.getOriginalFilename();
        String extension = "";
        if (originalFilename != null && originalFilename.contains(".")) {
            extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
        }

        // 使用指定的文件名或生成新的文件名
        String saveFilename;
        if (StringUtils.hasText(filename)) {
            saveFilename = filename;
        } else {
            // 生成带时间戳的文件名
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            saveFilename = sdf.format(new Date()) + extension;
        }

        // 保存文件 - 使用NIO非阻塞方式
        Path filePath = directory.resolve(saveFilename);

        // 使用缓冲区进行文件拷贝
        try (InputStream inputStream = file.getInputStream()) {
            Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
        }

        log.info("文件上传成功: {}", filePath);

        // 返回相对路径
        if (directory.equals(modelPath)) {
            return saveFilename;
        } else if (directory.equals(audioPath)) {
            return "origin_audio/" + saveFilename; // 注意这里使用正斜杠，更符合URL路径规范
        } else {
            return Paths.get(directory.getFileName().toString(), saveFilename).toString()
                    .replace("\\", "/"); // 替换为正斜杠
        }
    }

    /**
     * 根据视频文件名生成缩略图文件名
     *
     * @param videoFilename 视频文件名
     * @return 缩略图文件名
     */
    private String getThumbnailFilename(String videoFilename) {
        if (videoFilename.contains(".")) {
            return videoFilename.substring(0, videoFilename.lastIndexOf('.')) + ".jpg";
        }
        return videoFilename + ".jpg";
    }

    /**
     * 使用JavaCV生成视频缩略图
     *
     * @param videoPath 视频文件完整路径
     * @param thumbnailFilename 缩略图文件名
     * @return 缩略图相对路径
     * @throws IOException 如果生成缩略图失败
     */
    private String generateThumbnail(String videoPath, String thumbnailFilename) throws IOException {
        Path outputPath = thumbnailPath.resolve(thumbnailFilename);

        // 使用try-with-resources自动关闭资源
        try (FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videoPath);
             Java2DFrameConverter converter = new Java2DFrameConverter()) {

            // 初始化视频帧抓取器
            grabber.start();

            // 跳转到视频的第1秒（设置为1000000微秒）
            grabber.setTimestamp(1000000);

            // 抓取当前帧
            Frame frame = grabber.grabImage();
            if (frame == null) {
                // 如果第1秒没有帧，尝试抓取第一帧
                grabber.setTimestamp(0);
                frame = grabber.grabImage();

                // 如果仍然没有帧，抛出异常
                if (frame == null) {
                    throw new IOException("无法从视频中抓取帧");
                }
            }

            // 转换帧为BufferedImage
            BufferedImage bufferedImage = converter.convert(frame);

            // 保存为图片文件
            ImageIO.write(bufferedImage, "jpg", outputPath.toFile());

            log.info("成功生成缩略图: {}", outputPath);

            // 返回缩略图相对路径
            return thumbnailFilename;

        } catch (Exception e) {
            log.error("生成缩略图失败", e);
            throw new IOException("生成缩略图失败: " + e.getMessage(), e);
        }
    }
}